package com.sergio.accounting.service.impl;

import com.sergio.accounting.dto.RecordDTO;
import com.sergio.accounting.dto.StatisticsDTO;
import com.sergio.accounting.entity.Account;
import com.sergio.accounting.entity.Category;
import com.sergio.accounting.entity.Record;
import com.sergio.accounting.repository.CategoryRepository;
import com.sergio.accounting.repository.RecordRepository;
import com.sergio.accounting.service.RecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.sergio.accounting.dto.RecordResponseDTO;

/**
 * 记账记录服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional
public class RecordServiceImpl implements RecordService {

    private final RecordRepository recordRepository;
    private final CategoryRepository categoryRepository;
    private final com.sergio.accounting.service.AccountService accountService;

    @Override
    public Record addRecord(Long userId, RecordDTO recordDTO) {
        // 业务规则验证
        validateRecordDTO(recordDTO);
        
        // 验证分类是否存在且属于该用户
        Category category = categoryRepository.findById(recordDTO.getCategoryId())
                .orElseThrow(() -> new RuntimeException("分类不存在"));
        
        // 验证分类类型与记录类型是否匹配
        boolean isIncomeRecord = "INCOME".equalsIgnoreCase(recordDTO.getRecordType());
        boolean isIncomeCategory = category.getType() == Category.CategoryType.INCOME;
        if (isIncomeRecord != isIncomeCategory) {
            throw new RuntimeException("记录类型与分类类型不匹配");
        }

        // 创建记录
        Record record = new Record();
        record.setUserId(userId);
        record.setRecordType(Record.RecordType.valueOf(recordDTO.getRecordType().toUpperCase()));
        record.setAmount(recordDTO.getAmount());
        record.setCategory(category);
        record.setNote(recordDTO.getNote());
        record.setRecordDate(recordDTO.getRecordDate());
        
        // 设置账户ID并验证账户是否存在
        if (recordDTO.getAccountId() != null) {
            // 验证账户是否存在且属于当前用户
            if (!accountService.existsByIdAndUserId(recordDTO.getAccountId(), userId)) {
                throw new RuntimeException("账户不存在或不属于当前用户");
            }
            record.setAccountId(recordDTO.getAccountId());

            // 获取账户当前余额并设置交易前余额
            Account account = accountService.findById(recordDTO.getAccountId());
            if (account != null) {
                BigDecimal currentBalance = account.getCurrentBalance();
                record.setBalanceBefore(currentBalance);

                // 更新账户余额
                boolean isIncome = "INCOME".equalsIgnoreCase(recordDTO.getRecordType());
                accountService.updateAccountBalance(recordDTO.getAccountId(), recordDTO.getAmount(), isIncome);
                
                // 获取更新后的账户余额
                Account updatedAccount = accountService.findById(recordDTO.getAccountId());
                if (updatedAccount != null) {
                    record.setBalanceAfter(updatedAccount.getCurrentBalance());
                } else {
                    // Fallback if updated account is null (should not happen if updateAccountBalance works)
                    BigDecimal balanceAfter = isIncome 
                        ? currentBalance.add(recordDTO.getAmount())
                        : currentBalance.subtract(recordDTO.getAmount());
                    record.setBalanceAfter(balanceAfter);
                }
                log.info("账户余额已更新: 账户ID={}, 金额={}, 收入={}, 交易后余额={}", 
                        recordDTO.getAccountId(), recordDTO.getAmount(), isIncome, record.getBalanceAfter());
            }
        }

        Record savedRecord = recordRepository.save(record);
        
        log.info("记账记录添加成功: 用户ID={}, 金额={}, 类型={}, 账户ID={}", userId, recordDTO.getAmount(), recordDTO.getRecordType(), recordDTO.getAccountId());
        return savedRecord;
    }
    
    /**
     * 验证记录数据
     */
    private void validateRecordDTO(RecordDTO recordDTO) {
        if (recordDTO.getAmount() == null || recordDTO.getAmount().compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("金额必须大于0");
        }
        
        if (recordDTO.getAmount().compareTo(new BigDecimal("999999.99")) > 0) {
            throw new RuntimeException("金额不能超过999999.99");
        }
        
        if (recordDTO.getRecordDate() == null) {
            throw new RuntimeException("记录日期不能为空");
        }
        
        // 检查日期是否过于久远或未来
        LocalDate today = LocalDate.now();
        LocalDate recordDate = recordDTO.getRecordDate();
        if (recordDate.isAfter(today)) {
            throw new RuntimeException("记录日期不能是未来日期");
        }
        
        if (recordDate.isBefore(today.minusYears(10))) {
            throw new RuntimeException("记录日期不能超过10年前");
        }
    }

    @Override
    public Record updateRecord(Long userId, Long recordId, RecordDTO recordDTO) {
        Record record = recordRepository.findById(recordId)
                .orElseThrow(() -> new RuntimeException("记录不存在"));

        // 验证记录是否属于该用户
        if (!record.getUserId().equals(userId)) {
            throw new RuntimeException("无权操作此记录");
        }

        // 保存原始数据用于账户余额回滚
        BigDecimal originalAmount = record.getAmount();
        String originalRecordType = record.getRecordType().name();
        Long originalAccountId = record.getAccountId();

        // 验证分类是否存在
        if (recordDTO.getCategoryId() != null) {
            Category category = categoryRepository.findById(recordDTO.getCategoryId())
                    .orElseThrow(() -> new RuntimeException("分类不存在"));
            record.setCategory(category);
        }

        // 更新字段
        if (recordDTO.getRecordType() != null) {
            record.setRecordType(Record.RecordType.valueOf(recordDTO.getRecordType().toUpperCase()));
        }
        if (recordDTO.getAmount() != null) {
            record.setAmount(recordDTO.getAmount());
        }
        if (recordDTO.getNote() != null) {
            record.setNote(recordDTO.getNote());
        }
        if (recordDTO.getRecordDate() != null) {
            record.setRecordDate(recordDTO.getRecordDate());
        }
        // 处理账户ID更新 - 包括设置为null的情况
        if (recordDTO.getAccountId() != null) {
            // 验证账户是否存在且属于当前用户
            if (!accountService.existsByIdAndUserId(recordDTO.getAccountId(), userId)) {
                throw new RuntimeException("账户不存在或不属于当前用户");
            }
            record.setAccountId(recordDTO.getAccountId());
        } else {
            // 如果传入null，则设置为不计入账户
            record.setAccountId(null);
        }

        // 先回滚原始记录对账户余额的影响
        if (originalAccountId != null) {
            boolean wasIncome = "INCOME".equalsIgnoreCase(originalRecordType);
            // 回滚：如果原来是收入，现在要减去；如果原来是支出，现在要加回
            accountService.updateAccountBalance(originalAccountId, originalAmount, !wasIncome);
            log.info("回滚原始账户余额: 账户ID={}, 金额={}, 原为收入={}", originalAccountId, originalAmount, wasIncome);
        }

        Record updatedRecord = recordRepository.save(record);

        // 应用新记录对账户余额的影响
        if (updatedRecord.getAccountId() != null) {
            boolean isIncome = "INCOME".equalsIgnoreCase(updatedRecord.getRecordType().name());
            accountService.updateAccountBalance(updatedRecord.getAccountId(), updatedRecord.getAmount(), isIncome);
            log.info("更新新账户余额: 账户ID={}, 金额={}, 收入={}", updatedRecord.getAccountId(), updatedRecord.getAmount(), isIncome);
        }

        log.info("记账记录更新成功: 记录ID={}, 用户ID={}", recordId, userId);
        return updatedRecord;
    }

    @Override
    public boolean deleteRecord(Long userId, Long recordId) {
        Record record = recordRepository.findById(recordId)
                .orElseThrow(() -> new RuntimeException("记录不存在"));

        // 验证记录是否属于该用户
        if (!record.getUserId().equals(userId)) {
            throw new RuntimeException("无权操作此记录");
        }

        // 回滚账户余额
        if (record.getAccountId() != null) {
            boolean wasIncome = "INCOME".equalsIgnoreCase(record.getRecordType().name());
            // 删除记录：如果原来是收入，现在要减去；如果原来是支出，现在要加回
            accountService.updateAccountBalance(record.getAccountId(), record.getAmount(), !wasIncome);
            log.info("删除记录回滚账户余额: 账户ID={}, 金额={}, 原为收入={}", record.getAccountId(), record.getAmount(), wasIncome);
        }

        recordRepository.delete(record);
        log.info("记账记录删除成功: 记录ID={}, 用户ID={}", recordId, userId);
        return true;
    }

    @Override
    @Transactional(readOnly = true)
    public Record findById(Long id) {
        return recordRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("记录不存在"));
    }

    @Override
    @Transactional(readOnly = true)
    public List<Record> findByUserId(Long userId) {
        return recordRepository.findByUserIdOrderByRecordDateDesc(userId);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Record> findByUserIdAndType(Long userId, String recordType) {
        Record.RecordType type = Record.RecordType.valueOf(recordType.toUpperCase());
        return recordRepository.findByUserIdAndRecordTypeOrderByRecordDateDesc(userId, type);
    }

    @Override
    @Transactional(readOnly = true)
    public List<Record> findByUserIdAndDateRange(Long userId, LocalDate startDate, LocalDate endDate) {
        return recordRepository.findByUserIdAndDateRange(userId, startDate, endDate);
    }

    @Override
    @Transactional(readOnly = true)
    public List<RecordResponseDTO> findByUserIdAndDateRangeAsDTO(Long userId, LocalDate startDate, LocalDate endDate) {
        List<Record> records = recordRepository.findByUserIdAndDateRange(userId, startDate, endDate);
        return records.stream()
                .map(this::convertToResponseDTO)
                .collect(Collectors.toList());
    }

    /**
     * 将Record实体转换为RecordResponseDTO
     */
    private RecordResponseDTO convertToResponseDTO(Record record) {
        RecordResponseDTO.RecordResponseDTOBuilder builder = RecordResponseDTO.builder()
                .id(record.getId())
                .userId(record.getUserId())
                .recordType(record.getRecordType().name())
                .amount(record.getAmount())
                .categoryId(record.getCategory().getId())
                .categoryName(record.getCategory().getName())
                .categoryIcon(record.getCategory().getIcon())
                .categoryType(record.getCategory().getType().name())
                .note(record.getNote())
                .recordDate(record.getRecordDate())
                .createdAt(record.getCreatedAt())
                .updatedAt(record.getUpdatedAt());
        
        // 添加账户信息
        log.debug("处理记录ID={}的账户信息, Account对象={}, AccountId={}", 
                  record.getId(), record.getAccount(), record.getAccountId());
        
        // 设置余额信息（使用记录中存储的历史余额）
        if (record.getBalanceBefore() != null) {
            builder.accountBalanceBefore(record.getBalanceBefore());
        }
        if (record.getBalanceAfter() != null) {
            builder.accountBalanceAfter(record.getBalanceAfter());
        }
        
        if (record.getAccount() != null) {
            log.debug("使用已加载的Account对象: {}", record.getAccount().getName());
            builder.accountId(record.getAccount().getId())
                   .accountName(record.getAccount().getName())
                   .accountIcon(record.getAccount().getIcon())
                   .accountType(record.getAccount().getAccountType().name());
        } else if (record.getAccountId() != null) {
            // 如果Record中没有加载Account对象，但有accountId，则查询账户信息
            log.debug("Account对象未加载，尝试查询accountId={}", record.getAccountId());
            try {
                Account account = accountService.findById(record.getAccountId());
                if (account != null) {
                    log.debug("成功查询到账户: name={}, type={}", account.getName(), account.getAccountType());
                    builder.accountId(account.getId())
                           .accountName(account.getName())
                           .accountIcon(account.getIcon())
                           .accountType(account.getAccountType().name());
                }
            } catch (Exception e) {
                log.warn("获取账户信息失败: accountId={}, error={}", record.getAccountId(), e.getMessage());
            }
        } else {
            log.debug("记录ID={}没有关联账户", record.getId());
        }
        
        return builder.build();
    }

    @Override
    @Transactional(readOnly = true)
    public List<Record> findByUserIdAndTypeAndDateRange(Long userId, String recordType, LocalDate startDate, LocalDate endDate) {
        Record.RecordType type = Record.RecordType.valueOf(recordType.toUpperCase());
        return recordRepository.findByUserIdAndTypeAndDateRange(userId, type, startDate, endDate);
    }

    @Override
    @Transactional(readOnly = true)
    public StatisticsDTO getStatistics(Long userId, LocalDate startDate, LocalDate endDate) {
        StatisticsDTO statistics = new StatisticsDTO();
        statistics.setOverview(getOverview(userId));
        statistics.setTrend(getTrend(userId, startDate, endDate));
        statistics.setCategoryStats(getCategoryStats(userId, "EXPENSE", startDate, endDate));
        statistics.setRanking(getRanking(userId, "EXPENSE", startDate, endDate));
        return statistics;
    }

    @Override
    @Transactional(readOnly = true)
    public StatisticsDTO.OverviewDTO getOverview(Long userId) {
        StatisticsDTO.OverviewDTO overview = new StatisticsDTO.OverviewDTO();

        // 获取所有记录
        List<Record> allRecords = recordRepository.findByUserId(userId);

        if (allRecords.isEmpty()) {
            overview.setTotalIncome(BigDecimal.ZERO);
            overview.setTotalExpense(BigDecimal.ZERO);
            overview.setBalance(BigDecimal.ZERO);
            overview.setTotalRecords(0L);
            overview.setAverageAmount(BigDecimal.ZERO);
            overview.setMaxAmount(BigDecimal.ZERO);
            overview.setCategoryCount(0);
            overview.setTotalDays(0);
            overview.setAvgDailyExpense(BigDecimal.ZERO);
            return overview;
        }

        // 计算总收入和总支出
        BigDecimal totalIncome = allRecords.stream()
                .filter(record -> record.getRecordType() == Record.RecordType.INCOME)
                .map(Record::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        overview.setTotalIncome(totalIncome);

        BigDecimal totalExpense = allRecords.stream()
                .filter(record -> record.getRecordType() == Record.RecordType.EXPENSE)
                .map(Record::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        overview.setTotalExpense(totalExpense);

        // 计算余额
        BigDecimal balance = totalIncome.subtract(totalExpense);
        overview.setBalance(balance);

        // 统计总记录数
        overview.setTotalRecords((long) allRecords.size());

        // 计算平均金额（所有记录的平均金额，不区分收支）
        BigDecimal overallTotalAmount = allRecords.stream()
                .map(Record::getAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal averageAmount = allRecords.isEmpty() ? BigDecimal.ZERO :
                overallTotalAmount.divide(BigDecimal.valueOf(allRecords.size()), 2, RoundingMode.HALF_UP);
        overview.setAverageAmount(averageAmount);

        // 查找最大金额记录
        BigDecimal maxAmount = allRecords.stream()
                .map(Record::getAmount)
                .max(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
        overview.setMaxAmount(maxAmount);

        // 统计分类数量 (假设每个记录都有一个分类，并且分类是唯一的)
        long distinctCategories = allRecords.stream()
                .map(record -> record.getCategory().getId())
                .distinct()
                .count();
        overview.setCategoryCount((int) distinctCategories);

        // 计算记账天数
        long totalDays = allRecords.stream()
                .map(Record::getRecordDate)
                .distinct()
                .count();
        overview.setTotalDays((int) totalDays);

        // 计算日均支出
        BigDecimal avgDailyExpense = totalDays > 0 ?
                totalExpense.divide(BigDecimal.valueOf(totalDays), 2, RoundingMode.HALF_UP) :
                BigDecimal.ZERO;
        overview.setAvgDailyExpense(avgDailyExpense);

        return overview;
    }

    @Override
    @Transactional(readOnly = true)
    public List<StatisticsDTO.TrendDTO> getTrend(Long userId, LocalDate startDate, LocalDate endDate) {
        List<Object[]> dailyStats = recordRepository.getDailyStatsByUserIdAndDateRange(userId, startDate, endDate);
        List<StatisticsDTO.TrendDTO> trendList = new ArrayList<>();

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");

        for (Object[] stat : dailyStats) {
            LocalDate date = (LocalDate) stat[0];
            BigDecimal income = (BigDecimal) stat[1];
            BigDecimal expense = (BigDecimal) stat[2];
            BigDecimal balance = income.subtract(expense);

            StatisticsDTO.TrendDTO trend = new StatisticsDTO.TrendDTO();
            trend.setDate(date.format(formatter));
            trend.setIncome(income);
            trend.setExpense(expense);
            trend.setBalance(balance);
            trendList.add(trend);
        }

        return trendList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<StatisticsDTO.CategoryStatsDTO> getCategoryStats(Long userId, String recordType, LocalDate startDate, LocalDate endDate) {
        Record.RecordType type = Record.RecordType.valueOf(recordType.toUpperCase());
        List<Object[]> categoryStats = recordRepository.getCategoryStatsByUserIdAndTypeAndDateRange(userId, type, startDate, endDate);

        // 计算总金额
        BigDecimal totalAmount = categoryStats.stream()
                .map(stat -> (BigDecimal) stat[3])
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        List<StatisticsDTO.CategoryStatsDTO> result = new ArrayList<>();
        String[] colors = {"#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7", "#DDA0DD", "#98D8C8", "#F7DC6F"};

        for (int i = 0; i < categoryStats.size(); i++) {
            Object[] stat = categoryStats.get(i);
            Long categoryId = (Long) stat[0];
            String categoryName = (String) stat[1];
            String categoryIcon = (String) stat[2];
            BigDecimal amount = (BigDecimal) stat[3];

            StatisticsDTO.CategoryStatsDTO categoryStat = new StatisticsDTO.CategoryStatsDTO();
            categoryStat.setCategoryId(categoryId);
            categoryStat.setCategoryName(categoryName);
            categoryStat.setCategoryIcon(categoryIcon);
            categoryStat.setAmount(amount);

            // 计算百分比
            if (totalAmount.compareTo(BigDecimal.ZERO) > 0) {
                BigDecimal percentage = amount.multiply(BigDecimal.valueOf(100))
                        .divide(totalAmount, 2, RoundingMode.HALF_UP);
                categoryStat.setPercentage(percentage);
            } else {
                categoryStat.setPercentage(BigDecimal.ZERO);
            }

            // 设置颜色
            categoryStat.setColor(colors[i % colors.length]);

            result.add(categoryStat);
        }

        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public List<StatisticsDTO.RankingDTO> getRanking(Long userId, String recordType, LocalDate startDate, LocalDate endDate) {
        List<StatisticsDTO.CategoryStatsDTO> categoryStats = getCategoryStats(userId, recordType, startDate, endDate);
        List<StatisticsDTO.RankingDTO> rankingList = new ArrayList<>();

        for (int i = 0; i < categoryStats.size(); i++) {
            StatisticsDTO.CategoryStatsDTO categoryStat = categoryStats.get(i);
            
            StatisticsDTO.RankingDTO ranking = new StatisticsDTO.RankingDTO();
            ranking.setCategoryName(categoryStat.getCategoryName());
            ranking.setCategoryIcon(categoryStat.getCategoryIcon());
            ranking.setAmount(categoryStat.getAmount());
            ranking.setRank(i + 1);
            
            rankingList.add(ranking);
        }

        return rankingList;
    }

    @Override
    @Transactional(readOnly = true)
    public List<RecordResponseDTO> findByAccountId(Long accountId, Long userId) {
        List<Record> records = recordRepository.findByAccountIdAndUserIdOrderByRecordDateDesc(accountId, userId);
        return records.stream()
                .map(this::convertToResponseDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public List<RecordResponseDTO> findByAccountIdAndDateRange(Long accountId, Long userId, LocalDate startDate, LocalDate endDate) {
        List<Record> records = recordRepository.findByAccountIdAndUserIdAndDateRange(accountId, userId, startDate, endDate);
        return records.stream()
                .map(this::convertToResponseDTO)
                .collect(Collectors.toList());
    }
}
